package com.xiechuang.helper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 文件管理助手
 * @Author: xiechuang
 * @Date: 2023/1/11
 */
public class FileHelper {

    private final static Logger logger = LoggerFactory.getLogger(FileHelper.class);

    public static final long ONE_KB = 1024;
    public static final long ONE_MB = ONE_KB * ONE_KB;
    private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
    // 默认扩展名
    private static String[] postfixs = new String[]{"JPG", "PNG", "JPEG", "GIF", "DOC", "DOCX", "XLS", "XLSX", "TXT", "ZIP", "RAR", "ISO"};

    /**
     * Number 文件大小 转换为 String 文件大小
     *
     * @param size
     * @return
     */
    public static String toString(long size) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        if (size < 1024) {
            fileSizeString = df.format((double) size) + "B";
        } else if (size < 1048576) {
            fileSizeString = df.format((double) size / 1024) + "K";
        } else if (size < 1073741824) {
            fileSizeString = df.format((double) size / 1048576) + "M";
        } else {
            fileSizeString = df.format((double) size / 1073741824) + "G";
        }
        return fileSizeString;
    }

    /**
     * 递归删除文件
     *
     * @param file
     */
    public static void drop(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                drop(files[i]);
            }
        }
        if (!file.delete()) {
            logger.info("删除文件失败");
        }
    }

    public static final void drop(File[] files) {
        for (File file : files) {
            drop(file);
        }
    }

    /**
     * 复制
     *
     * @param srcFile
     * @param destFile
     * @param preserveFileDate
     * @throws IOException
     */
    private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
        if (destFile.exists() && destFile.isDirectory()) {
            throw new IOException("Destination '" + destFile + "' exists but is a directory");
        }

        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel input = null;
        FileChannel output = null;
        try {
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            input = fis.getChannel();
            output = fos.getChannel();
            long size = input.size();
            long pos = 0;
            long count = 0;
            while (pos < size) {
                count = (size - pos) > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : (size - pos);
                pos += output.transferFrom(input, pos, count);
            }
        } finally {
            IOHelper.closeQuietly(output);
            IOHelper.closeQuietly(fos);
            IOHelper.closeQuietly(input);
            IOHelper.closeQuietly(fis);
        }

        if (srcFile.length() != destFile.length()) {
            throw new IOException("Failed to copy full contents from '" +
                    srcFile + "' to '" + destFile + "'");
        }
        if (preserveFileDate) {
            if (!destFile.setLastModified(srcFile.lastModified())) {
                logger.info("设置最后体改日期失败");
            }
        }
    }


    /**
     * 递归 Copy 文件
     *
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copy(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
        if (srcFile == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destFile == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (srcFile.exists() == false) {
            throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
        }
        if (srcFile.isDirectory()) {
            throw new IOException("Source '" + srcFile + "' exists but is a directory");
        }
        if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
            throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
        }
        File parentFile = destFile.getParentFile();
        if (parentFile != null) {
            if (!parentFile.mkdirs() && !parentFile.isDirectory()) {
                throw new IOException("Destination '" + parentFile + "' directory cannot be created");
            }
        }
        if (destFile.exists() && destFile.canWrite() == false) {
            throw new IOException("Destination '" + destFile + "' exists but is read-only");
        }
        doCopyFile(srcFile, destFile, preserveFileDate);
    }

    public static void copy(File srcFile, File destFile) throws IOException {
        copy(srcFile, destFile, true);
    }

    public static boolean move(File srcFile, File destFile) {
        try {
            if (srcFile == null) {
                throw new NullPointerException("Source must not be null");
            }
            if (destFile == null) {
                throw new NullPointerException("Destination must not be null");
            }
            if (!srcFile.exists()) {
                throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
            }
            if (srcFile.isDirectory()) {
                throw new IOException("Source '" + srcFile + "' is a directory");
            }
            if (destFile.exists()) {
                throw new Exception("Destination '" + destFile + "' already exists");
            }
            if (destFile.isDirectory()) {
                throw new IOException("Destination '" + destFile + "' is a directory");
            }
            boolean rename = srcFile.renameTo(destFile);
            if (!rename) {
                copy(srcFile, destFile);
                if (!srcFile.delete()) {
                    drop(destFile);
                    throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
                }
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage() + "：", ex);
            return false;
        }
        return true;
    }

    /**
     * 获取文件后缀
     *
     * @param filename
     * @return
     */
    public static String getPostfix(String filename) {
        if (ValidHelper.isEmpty(filename)) {
            return null;
        } else if (filename.indexOf("?") != -1) {
            filename = filename.substring(0, filename.lastIndexOf("?"));
        }
        if (filename.lastIndexOf(".") != -1) {
            return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
        } else {
            for (String postfix : postfixs) {
                if (filename.toLowerCase().endsWith(postfix)) {
                    return postfix;
                }
            }
            return null;
        }
    }

    /**
     * 创建文件夹
     *
     * @param file1
     * @param file2
     * @return
     */
    public static File mkdirs(String file1, String file2) {
        File file = new File(file1, file2);
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }

    public static void write(String filepath, InputStream stream) throws Exception {
        write(new File(filepath), stream);
    }

    public static void write(File file, InputStream stream) throws Exception {
        if (file.exists()) {
            if (!file.delete()) {
                logger.info("删除文件失败");
            }
        }
        try {
            FileOutputStream output = new FileOutputStream(file);
            try {
                IOHelper.copy(stream, output);
            } finally {
                IOHelper.closeQuietly(output);
            }
        } finally {
            IOHelper.closeQuietly(stream);
        }
    }

    /**
     * 从input里面读取数据然后写入output，读完后自动关闭流
     *
     * @param input
     * @param output
     * @throws IOException
     */
    public static void write(InputStream input, OutputStream output) throws IOException {
        write(input, output, true);
    }

    /**
     * 自动从input里面读数据，然后写到output里面去
     *
     * @param input
     * @param output
     * @param close
     * @throws IOException
     */
    public static void write(InputStream input, OutputStream output, boolean close) throws IOException {
        byte[] b = new byte[1024];
        int len = input.read(b);
        while (len != -1) {
            output.write(b, 0, len);
            len = input.read(b);
        }
        output.flush();
        if (close) {
            input.close();
            output.close();
        }
    }

    public static byte[] read(File file) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            return IOHelper.read(fis);
        } catch (Exception ex) {
            logger.error(ex.getMessage() + "：", ex);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (Exception ex) {
                logger.error(ex.getMessage() + "：", ex);
            }
        }
        return new byte[]{};
    }

    public static String read(File file, String charset) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            return IOHelper.read(fis, charset);
        } catch (Exception ex) {
            logger.error(ex.getMessage() + "：", ex);
            return "";
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (Exception ex) {
                logger.error(ex.getMessage() + "：", ex);
            }
        }
    }

    /**
     * 递归读取文件
     *
     * @param files
     * @param parent
     * @param filter
     * @return
     */
    public static List<File> recursion(List<File> files, File parent, FileFilter filter) {
        if (null == files) {
            files = new ArrayList<File>();
        }
        if (parent.isDirectory()) {
            File[] cFile = null == filter ? parent.listFiles() : parent.listFiles(filter);
            for (File file : cFile) {
                recursion(files, file, filter);
            }
        } else {
            files.add(parent);
        }
        return files;
    }

}
